Skip to content

mergeConcatSwitchExhaustMap

rxjs: mergeMap, switchMap, concatMap, exhaustMap

Problem

Imagine you have to get a list of articles, and when you click on an article you need to get all the comments for that article.

The naive approve will be to do that:

typescript
fetcMyArticle().pipe(
    map(article => {
        return fetchComments(article.id);
    })
)

But this will not work, because the fetchComments will return an observable, and the map operator will return an observable of observables (higher order observables).

Solution

The solution is to use the mergeMap operator, which will subscribe to the inner observable and emit its values.

typescript
fetcMyArticle().pipe(
    mergeMap(article => {
        return fetchComments(article.id);
    })
)

This will work, but why ?

mergeMap vs switchMap vs concatMap vs exhaustMap

First of all, all of theses operators are composed of two operators, the map operator and the merge, switch, concat or exhaust operator.

mergeMap: map + mergeAll switchMap: map + switchAll concatMap: map + concatAll exhaustMap: map + exhaustAll

So in our example:

typescript
fetcMyArticle().pipe(
    mergeMap(article => {
        return fetchComments(article.id);
    })
)

are equivalent to:

typescript
fetcMyArticle().pipe(
    map(article => {
        return fetchComments(article.id);
    }),
    mergeAll()
)

All of theses operators ending with All will described the asynchrone behavior that you want to use. So you need to choose which user experience you want to provide to your users.

Dumb schema

Legend: 🍖: will take 30 minutes 🍺: will take 5 minutes 🍔: will take 10 minutes

{ 🍖, 🍺, 🍔 } ----- concatMap ----- [🍖, 🍺, 🍔]
{ 🍖, 🍺, 🍔 } ----- mergeMap ----- [🍺, 🍔, 🍖]
{ 🍖, 🍺, 🍔 } ----- switchMap ----- [🍔]
{ 🍖, 🍺, 🍔 } ----- exhaustMap ----- [🍖]
  • ConcatMap will serve the request in the same order as they come.

  • mergeMap will serve the request as soon as they are ready.

  • switchMap will cancel the previous request if a new one comes

  • exhaustMap will ignore the new request if a request is already in progress.

How ?

Questions to ask yourself to choose the right operator:

  • Am i on the view or the data access ? (view: [switchMap,exhaustMap], data access: [mergeMap,concatMap])

  • Data access: Do in need to keep the order ? (concatMap)

  • Data access: Faster but without order ? (mergeMap)

  • View: Minimize request cost ? (exhaustMap)

  • View: Cancel previous request, premium UX ? (switchMap)

References